#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include "Log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"

int backlog = 10;

enum
{
    SOCK_ERROR = 1,
    BIND_ERROR,
    Listen_ERROR,
    ACCEPT_ERROR,
};

// extern Log lg;
Log lg;

class TcpServer;

class ThreadData
{
public:
    ThreadData(int fd, const std::string &ip, uint16_t port, TcpServer *ts)
        : fd_(fd), client_ip_(ip), client_port_(port), ts_(ts)
    {
    }

public:
    int fd_;
    std::string client_ip_;
    uint16_t client_port_;
    TcpServer *ts_;
};

// void *Routine(void *args)
// {
//     ThreadData *ptd = static_cast<ThreadData *>(args);
//     ptd->ts_->Server(ptd->fd_, ptd->client_ip_, ptd->client_port_);
//     delete ptd;
//     return nullptr;
// }

class TcpServer
{
public:
    TcpServer(uint16_t port, const std::string &ip = "0.0.0.0", int fd = -1)
        : ip_(ip), port_(port), listenfd_(fd)
    {
    }

    void Initial()
    {
        listenfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listenfd_ < 0)
        {
            lg(Fatal, "creat sockfd error, errno: %d, error string: %s", errno, strerror(errno));
            exit(SOCK_ERROR);
        }
        lg(Info, "creat sockfd success, listenfd: %d", listenfd_);

        int opt = 1;
        setsockopt(listenfd_, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &local.sin_addr);

        if (bind(listenfd_, (const sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, errno: %d, error string: %s", errno, strerror(errno));
            exit(BIND_ERROR);
        }
        lg(Info, "bind success, listenfd_: %d", listenfd_); // 顺便输出

        if (listen(listenfd_, backlog) < 0)
        {
            lg(Fatal, "listen error, errno: %d, error string: %s", errno, strerror(errno));
            exit(Listen_ERROR);
        }
        lg(Info, "listen success, listenfd_: %d", listenfd_);
    }

    void Start()
    {
        daemon();
        ThreadPool<Task>::GetInstance()->Start();
        // ThreadPool<int>::GetInstance()->Start();
        for (;;) // 需要使用循环，否则client端退出后，本程序也会退出，而client端将重连失败
        {
            sockaddr_in client;
            socklen_t len = sizeof(client);
            int fd = accept(listenfd_, (sockaddr *)&client, &len);
            if (fd < 0)
            {
                lg(Fatal, "accept error, errno: %d, error string: %s", errno, strerror(errno));
                exit(ACCEPT_ERROR);
            }
            // lg(Info, "accept success, fd:%d", fd);

            // close(listenfd_);

            char client_ip[32];
            inet_ntop(AF_INET, &client.sin_addr, client_ip, sizeof(client_ip)); // 是计数client_ip的大小而不是client
            // uint16_t client_port = client.sin_port;
            uint16_t client_port = ntohs(client.sin_port); // 转换为主机序列

            // 输出消息，提示链接成功
            lg(Info, "get a new link..., fd: %d, client_ip: %s, client_port: %d", fd, client_ip, client_port);

            // 单进程

            // close(listenfd_);
            // Server(fd, client_ip, client_port);

            // 多进程

            // pid_t pid = fork();
            // if (pid == 0)
            // {
            //     // 不会用到，可直接关闭
            //     close(listenfd_);
            //     if (fork() > 0)
            //         exit(0);
            //     Server(fd, client_ip, client_port);
            //     close(fd);
            //     // 执行结束，直接退出
            //     exit(0);
            // }
            // // 将下方语句放在此处，就不用写多次
            // close(fd);
            // waitpid(pid, nullptr, 0);
            // // close(listenfd_);

            // 多线程

            // ThreadData td(fd, client_ip, client_port, this);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, &td);
            // // 在函数中将线程分离后，主线程就无需等待了
            // // pthread_join(tid, nullptr);

            // 线程池
            Task t(fd, client_ip, client_port);
            ThreadPool<Task>::GetInstance()->Push(t);

            // ThreadPool<int>::GetInstance()->Push(1);
        }

        close(listenfd_);
    }

    static void *Routine(void *args)
    {
        // 直接解关系，这样就无需等待了
        pthread_detach(pthread_self());
        ThreadData *ptd = static_cast<ThreadData *>(args);
        ptd->ts_->Server(ptd->fd_, ptd->client_ip_, ptd->client_port_);
        // delete ptd;
        return nullptr;
    }

    void Server(int fd, const std::string &client_ip, uint16_t client_port)
    {
        char buff[1024];
        while (true)
        {
            ssize_t sz = read(fd, buff, sizeof(buff));
            if (sz > 0)
            {
                buff[sz] = 0;
                std::cout << "client say: " << buff << std::endl;
                std::string res = "server say: ";
                res += buff;
                write(fd, res.c_str(), res.size());
            }
            else if (sz == 0)
            {
                // 为了美观，不再输出消息
                //  std::cout << "[" << client_ip << ":" << client_port << "]" << "quit" << std::endl;

                // 输出退出消息
                lg(Info, "%s:%d quit, server close sockfd: %d", client_ip.c_str(), client_port, fd);
                break;
            }
            else
            {
                lg(Warning, "read error, errno: %d, error string: %s", errno, strerror(errno));
                // continue;

                // 可直接退出
                break;
            }
        }
        close(fd);
    }

private:
    int listenfd_;
    int port_;
    std::string ip_;
};